package loger

import (
	"fmt"
	"os"
	"runtime"
	"strings"
	"time"
)

const (
	CSTR_Uid = "UID"
	CSTR_Gid = "GID"
	CSTR_Cmd = "CMD"
)

type Wrapper struct {
	logger *Logger

	day   int
	level Level
	file  *os.File
	fname string
	pname string
}

func NewWrapper(name string, level Level) *Wrapper {
	pname, fname := "", name
	for i := len(name) - 1; i > 0; i-- {
		if name[i] == '/' {
			pname, fname = name[:i+1], name[i+1:]
			break
		}
	}
	year, month, day := time.Now().Date()
	sname := fmt.Sprintf("%s_%04d%02d%02d.log", fname, year, month, day)
	file, err := os.OpenFile(pname+sname, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create file err:%s", err.Error())
		return nil
	}

	this := new(Wrapper)
	this.fname = fname
	this.pname = pname
	this.level = level
	this.file = file
	this.day = day

	this.logger = New()
	this.logger.SetLevel(level)
	this.logger.SetOutput(this.file)
	this.logger.SetFormatter(this)
	this.logger.AddHook(this)

	return this
}

func (this *Wrapper) Format(entry *Entry) ([]byte, error) {
	data := []string{}
	data = append(data, entry.Time.Format(timeformat))
	data = append(data, entry.Level.String())
	data = append(data, this.GetCaller())

	if entry.Data != nil {
		if v, ok := entry.Data[CSTR_Uid]; ok {
			data = append(data, CSTR_Uid+":"+v.(string))
		}
		if v, ok := entry.Data[CSTR_Gid]; ok {
			data = append(data, CSTR_Gid+":"+v.(string))
		}
		if v, ok := entry.Data[CSTR_Cmd]; ok {
			data = append(data, v.(string))
		}
	}

	data = append(data, entry.Message+"\n")

	return []byte(strings.Join(data, "|")), nil
}

func (this *Wrapper) Levels() []Level { return AllLevels }

func (this *Wrapper) Fire(entry *Entry) error {
	now := time.Now()
	year, month, day := now.Date()
	if day != this.day {
		this.file.Close()
		fname := fmt.Sprintf("%s_%04d%02d%02d.log", this.fname, year, month, day)
		this.file, _ = os.OpenFile(this.pname+fname, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		this.day = day
		this.logger.SetOutput(this.file)
	}
	return nil
}

func (this *Wrapper) WithField(strUid, strGid, strCmd string) *Entry {
	return this.logger.WithFields(map[string]interface{}{CSTR_Uid: strUid, CSTR_Gid: strGid, CSTR_Cmd: strCmd})
}

func (this *Wrapper) WithoutField() *Entry {
	return this.logger.WithFields(nil)
}

func (this *Wrapper) GetCaller() string {
	_, file, line, ok := runtime.Caller(4)
	if !ok {
		file = "x"
		line = 0
	}
	for i := len(file) - 1; i > 0; i-- {
		if file[i] == '/' {
			file = file[i+1:]
			break
		}
	}
	var b [20]byte
	pos := len(b) - 1
	for line >= 10 {
		q := line / 10
		b[pos] = byte('0' + line - q*10)
		pos--
		line = q
	}
	b[pos] = byte('0' + line)
	b[pos-1] = byte(':')
	return file + string(b[pos-1:])
}

func (this *Wrapper) SetLevel(level Level) { this.logger.SetLevel(level) }
func (this *Wrapper) GetLevel() Level      { return this.logger.GetLevel() }
func (this *Wrapper) AddHook(hook Hook)    { this.logger.AddHook(hook) }
